package com.kim.common.util;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * @Author : xhh
 * @Date : 2024-12-25
 * @Desc : 环形队列 - 设备离线判定
 * 1、预设一个长度为10000的数组（按实际业务定义长度）
 * 2、每个数组存放一个Set集合
 * 3、维护一个游标cur，从0到9999递增，到达9999时，重置为0（启动一个线程执行）
 * 4、维护一个map，记录所有设备ID存放的数组位置，方便查找
 * 5、监听到设备心跳时，先将原来的数据从指定位置的集合中删除，通过计算当前游标位置和keepAlive寻找合适的位置将设备ID放入
 * 6、当游标指向某个位置a时，a位置的集合中的所有设备全部判定为离线，并清空该位置的集合
 **/
 
public class CircleQueue<T> {
 
    //线程安全锁
    Lock lock = new ReentrantLock();
 
    //初始环形队列大小
    private int capacity = 10000;
 
    //当前环形队列所在节点
    private volatile int currentIndex = 0;
 
    //数据所在节点
    private Map<T,Integer> dataIndex = new HashMap<>();
 
    //环形队列
    private Set<T>[] array;
 
    public CircleQueue(){
        array = new HashSet[capacity];
    }
 
    public CircleQueue(int capacity){
        this.capacity = capacity;
        array = new HashSet[capacity];
    }
 
    /**
     * 向环形队列中添加元素
     * @param t
     * @param offset 偏移量，基于游标
     */
    public void add(T t, int offset){
        int index = currentIndex + offset;
        if(index >= capacity){
            index = index - capacity;
        }
        try {
            lock.lock();
            //判断数据是否存在
            if(dataIndex.containsKey(t)){
                Set<T> old  =  array[dataIndex.get(t)];
                old.remove(t);
            }
            //获取当前节点的队列
            Set<T> set = array[index];
            if(null == set){
                set = new HashSet<>();
                array[index] = set;
            }
            set.add(t);
            //更新新的节点位置
            dataIndex.put(t,index);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
 
    /**
     * 下移一格，到9999重新置为0
     */
    public void next(){
        int cur = currentIndex + 1;
        if(cur >= capacity){
            cur = cur - capacity;
        }
        currentIndex = cur;
        System.out.println("当前游标位置：" + currentIndex);
    }
 
    /**
     * 获取当前游标指向的元素集合
     * @return
     */
    public Set<T> getAndDeleteData(){
        Set<T> set = null;
        try {
            lock.lock();
            set = array[currentIndex];
            return set;
        }finally {
            // 将集合中所有的元素移除
            array[currentIndex] = new HashSet<>();
            if(set != null && set.size()>0){
                set.forEach(t -> {
                    dataIndex.remove(t);
                });
            }
            lock.unlock();
        }
    }
 
    public int getIndex(T t){
        if(dataIndex.containsKey(t)){
            return dataIndex.get(t);
        }
        return -1;
    }
}